package com.momochen.alarmclockdemo;

import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;

import com.momochen.alarmclockdemo.bean.AlarmBean;
import com.momochen.alarmclockdemo.bean.DaysOfWeek;
import com.momochen.alarmclockdemo.db.AlarmBeanDaoHelper;

import org.greenrobot.greendao.query.LazyList;

import java.util.Calendar;

import static com.momochen.alarmclockdemo.AlarmConstant.ALARM_ALERT_ACTION;
import static com.momochen.alarmclockdemo.AlarmConstant.ALARM_RAW_DATA;
import static com.momochen.alarmclockdemo.AlarmConstant.PREFERENCES;
import static com.momochen.alarmclockdemo.AlarmConstant.PREF_SNOOZE_ID;
import static com.momochen.alarmclockdemo.AlarmConstant.PREF_SNOOZE_TIME;

/**
 * Created by momochen on 2017/7/9.
 */

public class AlarmControl {

    /**
     * 添加闹钟
     *
     * @param context
     * @param alarm
     */
    public static void addAlarm(Context context, AlarmBean alarm) {
        if (alarm == null) {
            return;
        }

        long timeInMillis = calculateAlarm(alarm);
        if (alarm.getEnabled()) {
            clearSnoozeIfNeeded(context, timeInMillis);
        }
        setNextAlert(context);
    }

    /**
     * Called at system startup, on time/timezone change, and whenever
     * the user changes alarm settings.  Activates snooze if set,
     * otherwise loads all alarms, activates next alert.
     */
    public static void setNextAlert(final Context context) {
        if (!enableSnoozeAlert(context)) {
            AlarmBean alarm = calculateNextAlert(context);
            if (alarm != null) {
                enableAlert(context, alarm, alarm.getTimeStamp());
            } else {
                disableAlert(context);
            }
        }
    }

    /**
     * Sets alert in AlarmManger and StatusBar.  This is what will
     * actually launch the alert when the alarm triggers.
     *
     * @param alarm          Alarm.
     * @param atTimeInMillis milliseconds since epoch
     */
    public static void enableAlert(Context context, final AlarmBean alarm, final long atTimeInMillis) {
        AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

//        Intent intent = new Intent(ALARM_ALERT_ACTION);
//        intent.putExtra(ALARM_RAW_DATA, alarm);

        Intent intent = new Intent(ALARM_ALERT_ACTION);
        Bundle bundle = new Bundle();
        bundle.putSerializable(ALARM_RAW_DATA, alarm);
        intent.putExtra("BUNDLE", bundle);
//                intent.putExtra(ALARM_RAW_DATA, new AlarmBean());

        PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);

        am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender);

        System.out.println("===enableAlert>" + atTimeInMillis);
    }

    /**
     * Disables alert in AlarmManger and StatusBar.
     */
    static void disableAlert(Context context) {
        System.out.println("===>disableAlert");

        AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        PendingIntent sender = PendingIntent.getBroadcast(context, 0, new Intent(ALARM_ALERT_ACTION), PendingIntent.FLAG_CANCEL_CURRENT);
        am.cancel(sender);
//        setStatusBarIcon(context, false);
//        saveNextAlarm(context, "");
    }

    private static void clearSnoozeIfNeeded(Context context, long alarmTime) {
        // If this alarm fires before the next snooze, clear the snooze to
        // enable this alarm.
        SharedPreferences prefs = context.getSharedPreferences(PREFERENCES, 0);
        long snoozeTime = prefs.getLong(PREF_SNOOZE_TIME, 0);
        if (alarmTime < snoozeTime) {
            clearSnoozePreference(context, prefs);
        }
    }

    public static void saveSnoozeAlert(final Context context, final int id,
                                       final long time) {
        SharedPreferences prefs = context.getSharedPreferences(PREFERENCES, 0);
        if (id == -1) {
            clearSnoozePreference(context, prefs);
        } else {
            SharedPreferences.Editor ed = prefs.edit();
            ed.putInt(PREF_SNOOZE_ID, id);
            ed.putLong(PREF_SNOOZE_TIME, time);
            ed.apply();
        }
        // Set the next alert after updating the snooze.
        setNextAlert(context);
    }

    /**
     * Disable the snooze alert if the given id matches the snooze id.
     */
    public static void disableSnoozeAlert(final Context context, final long id) {
        SharedPreferences prefs = context.getSharedPreferences(PREFERENCES, 0);
        int snoozeId = prefs.getInt(PREF_SNOOZE_ID, -1);
        if (snoozeId == -1) {
            // No snooze set, do nothing.
            return;
        } else if (snoozeId == id) {
            // This is the same id so clear the shared prefs.
            clearSnoozePreference(context, prefs);
        }
    }

    // Helper to remove the snooze preference. Do not use clear because that
    // will erase the clock preferences. Also clear the snooze notification in
    // the window shade.
    private static void clearSnoozePreference(final Context context,
                                              final SharedPreferences prefs) {
        final int alarmId = prefs.getInt(PREF_SNOOZE_ID, -1);
        if (alarmId != -1) {
            NotificationManager nm = (NotificationManager)
                    context.getSystemService(Context.NOTIFICATION_SERVICE);
            nm.cancel(alarmId);
        }

        final SharedPreferences.Editor ed = prefs.edit();
        ed.remove(PREF_SNOOZE_ID);
        ed.remove(PREF_SNOOZE_TIME);
        ed.apply();
    }

    ;

    /**
     * If there is a snooze set, enable it in AlarmManager
     *
     * @return true if snooze is set
     */
    private static boolean enableSnoozeAlert(final Context context) {
        SharedPreferences prefs = context.getSharedPreferences(PREFERENCES, 0);

        int id = prefs.getInt(PREF_SNOOZE_ID, -1);
        if (id == -1) {
            return false;
        }
        long time = prefs.getLong(PREF_SNOOZE_TIME, -1);

        // Get the alarm from the db.
        final AlarmBean alarm = getAlarm(id);
        if (alarm == null) {
            return false;
        }
        // The time in the database is either 0 (repeating) or a specific time
        // for a non-repeating alarm. Update this value so the AlarmReceiver
        // has the right time to compare.
        alarm.setTimeStamp(time);

        enableAlert(context, alarm, time);
        return true;
    }

    private static long calculateAlarm(AlarmBean alarm) {
        return calculateAlarm(alarm.getHour(), alarm.getMinutes(), new DaysOfWeek(alarm.getDaysOfWeek())).getTimeInMillis();
    }

    /**
     * Given an alarm in hours and minutes, return a time suitable for
     * setting in AlarmManager.
     */
    static Calendar calculateAlarm(int hour, int minute, DaysOfWeek daysOfWeek) {

        // start with now
        Calendar c = Calendar.getInstance();
        c.setTimeInMillis(System.currentTimeMillis());

        int nowHour = c.get(Calendar.HOUR_OF_DAY);
        int nowMinute = c.get(Calendar.MINUTE);

        // if alarm is behind current time, advance one day
        if (hour < nowHour || hour == nowHour && minute <= nowMinute) {
            c.add(Calendar.DAY_OF_YEAR, 1);
        }
        c.set(Calendar.HOUR_OF_DAY, hour);
        c.set(Calendar.MINUTE, minute);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);

        int addDays = daysOfWeek.getNextAlarm(c);
        if (addDays > 0) c.add(Calendar.DAY_OF_WEEK, addDays);
        return c;
    }

    /**
     * Return an Alarm object representing the alarm id in the database.
     * Returns null if no alarm exists.
     */
    public static AlarmBean getAlarm(long alarmId) {
        return AlarmBeanDaoHelper.queryById(alarmId);
    }

    public static AlarmBean calculateNextAlert(final Context context) {
        AlarmBean alarm = null;
        long minTime = Long.MAX_VALUE;
        long now = System.currentTimeMillis();
        LazyList<AlarmBean> lazyList = AlarmBeanDaoHelper.queryAllByEnabled();
        if (lazyList == null) {
            return alarm;
        }

        try {
            for (int i = 0; i < lazyList.size(); i++) {
                AlarmBean a = lazyList.get(i);
                if (a == null) {
                    continue;
                }

                if (a.getTimeStamp() == 0) {
                    a.setTimeStamp(calculateAlarm(a));
                } else if (a.getTimeStamp() < now) {
                    Log.v("wangxianming", "Disabling expired alarm set for ");
                    // Expired alarm, disable it and move along.
                    enableAlarmInternal(context, a, false);
                    continue;
                }
                if (a.getTimeStamp() < minTime) {
                    minTime = a.getTimeStamp();
                    alarm = a;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (lazyList != null) {
                lazyList.close();
            }
        }
        return alarm;
    }

    /**
     * A convenience method to enable or disable an alarm.
     *
     * @param id      corresponds to the _id column
     * @param enabled corresponds to the ENABLED column
     */

    public static void enableAlarm(final Context context, final long id, boolean enabled) {
        enableAlarmInternal(context, id, enabled);
        setNextAlert(context);
    }

    private static void enableAlarmInternal(final Context context,
                                            final long id, boolean enabled) {
        enableAlarmInternal(context, getAlarm(id), enabled);
    }

    private static void enableAlarmInternal(final Context context,
                                            final AlarmBean alarm, boolean enabled) {
        if (alarm == null) {
            return;
        }
        int enable = alarm.getEnabled() ? 1 : 0;

        // If we are enabling the alarm, calculate alarm time since the time
        // value in Alarm may be old.
        long time = -1;
        if (enabled) {
            if (!new DaysOfWeek(alarm.getDaysOfWeek()).isRepeatSet()) {
                time = calculateAlarm(alarm);
            }
        } else {
            // Clear the snooze if the id matches.
            disableSnoozeAlert(context, alarm.getId());
        }

        AlarmBeanDaoHelper.updateAlarm(alarm.getId(), enable, time);
    }

    /**
     * Disables non-repeating alarms that have passed.  Called at
     * boot.
     */
    public static void disableExpiredAlarms(final Context context) {
        LazyList<AlarmBean> lazyList = AlarmBeanDaoHelper.queryAllByEnabled();
        if (lazyList == null) {
            return;
        }
        long now = System.currentTimeMillis();

        try {
            for (int i = 0; i < lazyList.size(); i++) {
                AlarmBean alarm = lazyList.get(i);
                if (alarm == null) {
                    continue;
                }

                if (alarm.getTimeStamp() != 0 && alarm.getTimeStamp() < now) {
                    enableAlarmInternal(context, alarm, false);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (lazyList != null) {
                lazyList.close();
            }
        }
    }
}
